{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffe08bad",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from dotenv import load_dotenv\n",
    "from openai import OpenAI\n",
    "import json\n",
    "from typing import List, Dict\n",
    "import gradio as gr\n",
    "import random\n",
    "\n",
    "load_dotenv(override=True)\n",
    "client = OpenAI()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2f24eb03",
   "metadata": {},
   "outputs": [],
   "source": [
    "LEGAL_TOPIC_SEEDS = [\n",
    "    \"criminal offenses and penalties\",\n",
    "    \"property rights and disputes\",\n",
    "    \"contract law and breach remedies\",\n",
    "    \"civil procedure and court processes\",\n",
    "    \"evidence admissibility rules\",\n",
    "    \"constitutional rights protections\",\n",
    "    \"family law and inheritance\",\n",
    "    \"corporate governance regulations\",\n",
    "    \"intellectual property protections\",\n",
    "    \"cyber crime and digital law\"\n",
    "]\n",
    "\n",
    "QUESTION_TYPES = [\n",
    "    \"definition\",\n",
    "    \"procedure\",\n",
    "    \"penalty\",\n",
    "    \"rights\",\n",
    "    \"obligations\",\n",
    "    \"exceptions\",\n",
    "    \"examples\"\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9256c3ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SyntheticLegalGenerator:\n",
    "    \"\"\"Generates synthetic legal content and sections\"\"\"\n",
    "    \n",
    "    def __init__(self, client: OpenAI, model: str = \"gpt-4o-mini\"):\n",
    "        self.client = client\n",
    "        self.model = model\n",
    "    \n",
    "    def generate_legal_section(self, topic: str) -> Dict[str, str]:\n",
    "        \"\"\"Generate a completely synthetic legal section\"\"\"\n",
    "        \n",
    "        prompt = f\"\"\"Create a SYNTHETIC (fictional but realistic) Indian legal section about: {topic}\n",
    "\n",
    "Generate:\n",
    "1. A section number (format: IPC XXX or CrPC XXX or IEA XXX)\n",
    "2. A clear title\n",
    "3. A detailed legal provision (2-3 sentences)\n",
    "\n",
    "Make it realistic but completely fictional. Use legal language.\n",
    "\n",
    "Format:\n",
    "SECTION: [number]\n",
    "TITLE: [title]\n",
    "PROVISION: [detailed text]\"\"\"\n",
    "\n",
    "        try:\n",
    "            response = self.client.chat.completions.create(\n",
    "                model=self.model,\n",
    "                messages=[\n",
    "                    {\"role\": \"system\", \"content\": \"You are a legal content generator creating synthetic Indian legal provisions for educational purposes.\"},\n",
    "                    {\"role\": \"user\", \"content\": prompt}\n",
    "                ],\n",
    "                temperature=0.8,\n",
    "                max_tokens=400\n",
    "            )\n",
    "            \n",
    "            content = response.choices[0].message.content.strip()\n",
    "            \n",
    "            # Parse the response\n",
    "            section_num = \"\"\n",
    "            title = \"\"\n",
    "            provision = \"\"\n",
    "            \n",
    "            for line in content.split('\\n'):\n",
    "                if line.startswith('SECTION:'):\n",
    "                    section_num = line.replace('SECTION:', '').strip()\n",
    "                elif line.startswith('TITLE:'):\n",
    "                    title = line.replace('TITLE:', '').strip()\n",
    "                elif line.startswith('PROVISION:'):\n",
    "                    provision = line.replace('PROVISION:', '').strip()\n",
    "            \n",
    "            return {\n",
    "                \"section_number\": section_num,\n",
    "                \"title\": title,\n",
    "                \"provision\": provision,\n",
    "                \"topic\": topic\n",
    "            }\n",
    "            \n",
    "        except Exception as e:\n",
    "            print(f\"Error generating section: {e}\")\n",
    "            return {\n",
    "                \"section_number\": \"IPC 000\",\n",
    "                \"title\": \"Error\",\n",
    "                \"provision\": f\"Failed to generate: {e}\",\n",
    "                \"topic\": topic\n",
    "            }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32be3d52",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SyntheticQAGenerator:\n",
    "    \"\"\"Generates Q&A pairs from synthetic legal sections\"\"\"\n",
    "    \n",
    "    def __init__(self, client: OpenAI, model: str = \"gpt-4o-mini\"):\n",
    "        self.client = client\n",
    "        self.model = model\n",
    "    \n",
    "    def generate_qa_pair(self, legal_section: Dict[str, str], question_type: str) -> Dict[str, str]:\n",
    "        \"\"\"Generate Q&A pair from synthetic legal section\"\"\"\n",
    "        \n",
    "        prompt = f\"\"\"Based on this SYNTHETIC legal section, create a {question_type}-type question and answer:\n",
    "\n",
    "Section: {legal_section['section_number']}\n",
    "Title: {legal_section['title']}\n",
    "Provision: {legal_section['provision']}\n",
    "\n",
    "Create ONE question (focusing on {question_type}) and a clear, accurate answer based on this provision.\n",
    "\n",
    "Format:\n",
    "Q: [question]\n",
    "A: [answer]\n",
    "\n",
    "Keep it educational and clear.\"\"\"\n",
    "\n",
    "        try:\n",
    "            response = self.client.chat.completions.create(\n",
    "                model=self.model,\n",
    "                messages=[\n",
    "                    {\"role\": \"system\", \"content\": \"You are creating educational Q&A pairs from synthetic legal content.\"},\n",
    "                    {\"role\": \"user\", \"content\": prompt}\n",
    "                ],\n",
    "                temperature=0.7,\n",
    "                max_tokens=350\n",
    "            )\n",
    "            \n",
    "            content = response.choices[0].message.content.strip()\n",
    "            \n",
    "            # Parse Q&A\n",
    "            question = \"\"\n",
    "            answer = \"\"\n",
    "            \n",
    "            for line in content.split('\\n'):\n",
    "                if line.startswith('Q:'):\n",
    "                    question = line[2:].strip()\n",
    "                elif line.startswith('A:'):\n",
    "                    answer = line[2:].strip()\n",
    "            \n",
    "            return {\n",
    "                \"section_number\": legal_section['section_number'],\n",
    "                \"section_title\": legal_section['title'],\n",
    "                \"provision\": legal_section['provision'],\n",
    "                \"question_type\": question_type,\n",
    "                \"question\": question,\n",
    "                \"answer\": answer\n",
    "            }\n",
    "            \n",
    "        except Exception as e:\n",
    "            print(f\"Error generating Q&A: {e}\")\n",
    "            return {\n",
    "                \"section_number\": legal_section['section_number'],\n",
    "                \"section_title\": legal_section['title'],\n",
    "                \"provision\": legal_section['provision'],\n",
    "                \"question_type\": question_type,\n",
    "                \"question\": \"Error generating question\",\n",
    "                \"answer\": \"Error generating answer\"\n",
    "            }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fe88708f",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SyntheticDataPipeline:\n",
    "    \"\"\"Complete pipeline for synthetic legal Q&A generation\"\"\"\n",
    "    \n",
    "    def __init__(self, legal_gen: SyntheticLegalGenerator, qa_gen: SyntheticQAGenerator):\n",
    "        self.legal_gen = legal_gen\n",
    "        self.qa_gen = qa_gen\n",
    "        self.dataset: List[Dict[str, str]] = []\n",
    "    \n",
    "    def generate_complete_entry(self, topic: str = None, question_type: str = None) -> Dict[str, str]:\n",
    "        \"\"\"Generate synthetic legal section + Q&A in one go\"\"\"\n",
    "        \n",
    "        # Pick random topic if not provided\n",
    "        if topic is None:\n",
    "            topic = random.choice(LEGAL_TOPIC_SEEDS)\n",
    "        \n",
    "        # Pick random question type if not provided\n",
    "        if question_type is None:\n",
    "            question_type = random.choice(QUESTION_TYPES)\n",
    "        \n",
    "        # Step 1: Generate synthetic legal section\n",
    "        legal_section = self.legal_gen.generate_legal_section(topic)\n",
    "        \n",
    "        # Step 2: Generate Q&A from that section\n",
    "        qa_pair = self.qa_gen.generate_qa_pair(legal_section, question_type)\n",
    "        \n",
    "        return qa_pair\n",
    "    \n",
    "    def generate_batch(self, count: int, progress_callback=None) -> List[Dict[str, str]]:\n",
    "        \"\"\"Generate multiple synthetic entries\"\"\"\n",
    "        batch = []\n",
    "        \n",
    "        for i in range(count):\n",
    "            if progress_callback:\n",
    "                progress_callback((i + 1) / count, desc=f\"Generating {i+1}/{count}...\")\n",
    "            \n",
    "            entry = self.generate_complete_entry()\n",
    "            batch.append(entry)\n",
    "            self.dataset.append(entry)\n",
    "        \n",
    "        return batch\n",
    "    \n",
    "    def save_dataset(self, filename: str = \"synthetic_legal_qa.json\") -> str:\n",
    "        \"\"\"Save dataset to JSON\"\"\"\n",
    "        try:\n",
    "            with open(filename, 'w', encoding='utf-8') as f:\n",
    "                json.dump(self.dataset, f, indent=2, ensure_ascii=False)\n",
    "            return f\"✅ Saved {len(self.dataset)} synthetic Q&A pairs to {filename}\"\n",
    "        except Exception as e:\n",
    "            return f\"❌ Error saving: {e}\"\n",
    "    \n",
    "    def get_summary(self) -> str:\n",
    "        \"\"\"Get dataset summary\"\"\"\n",
    "        if not self.dataset:\n",
    "            return \"No synthetic data generated yet.\"\n",
    "        \n",
    "        summary = f\"**Total Synthetic Q&A Pairs:** {len(self.dataset)}\\n\\n\"\n",
    "        summary += \"**Topics Covered:**\\n\"\n",
    "        \n",
    "        topics = {}\n",
    "        for entry in self.dataset:\n",
    "            topic = entry.get('section_title', 'Unknown')\n",
    "            topics[topic] = topics.get(topic, 0) + 1\n",
    "        \n",
    "        for topic, count in topics.items():\n",
    "            summary += f\"- {topic}: {count}\\n\"\n",
    "        \n",
    "        return summary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0822c49e",
   "metadata": {},
   "outputs": [],
   "source": [
    "legal_generator = SyntheticLegalGenerator(client)\n",
    "qa_generator = SyntheticQAGenerator(client)\n",
    "pipeline = SyntheticDataPipeline(legal_generator, qa_generator)\n",
    "\n",
    "print(\"✅ Synthetic data pipeline initialized!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9b86f15f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cell 8: UI functions with real-time progress updates\n",
    "def generate_single_synthetic(topic_choice: str, question_type: str, progress=gr.Progress()):\n",
    "    \"\"\"Generate single synthetic entry with real-time updates\"\"\"\n",
    "    \n",
    "    # Step 1: Generate legal section\n",
    "    progress(0.2, desc=\"🔍 Generating synthetic legal section...\")\n",
    "    yield \"⏳ Creating synthetic legal provision...\", pipeline.get_summary()\n",
    "    \n",
    "    legal_section = pipeline.legal_gen.generate_legal_section(topic_choice)\n",
    "    \n",
    "    # Show intermediate result\n",
    "    intermediate = f\"### 📜 Generated Section\\n\\n\"\n",
    "    intermediate += f\"**{legal_section['section_number']}**: {legal_section['title']}\\n\\n\"\n",
    "    intermediate += f\"_{legal_section['provision']}_\\n\\n\"\n",
    "    intermediate += \"⏳ Now generating Q&A pair...\"\n",
    "    \n",
    "    progress(0.5, desc=\"💭 Creating Q&A pair...\")\n",
    "    yield intermediate, pipeline.get_summary()\n",
    "    \n",
    "    # Step 2: Generate Q&A\n",
    "    qa_pair = pipeline.qa_gen.generate_qa_pair(legal_section, question_type)\n",
    "    pipeline.dataset.append(qa_pair)\n",
    "    \n",
    "    progress(0.9, desc=\"✨ Finalizing...\")\n",
    "    \n",
    "    # Final result\n",
    "    result = f\"### 🏛️ {qa_pair['section_number']}: {qa_pair['section_title']}\\n\\n\"\n",
    "    result += f\"**Provision:** {qa_pair['provision']}\\n\\n\"\n",
    "    result += f\"**Question Type:** _{qa_pair['question_type']}_\\n\\n\"\n",
    "    result += f\"**Q:** {qa_pair['question']}\\n\\n\"\n",
    "    result += f\"**A:** {qa_pair['answer']}\\n\\n\"\n",
    "    result += \"---\\n✅ **Added to dataset!**\"\n",
    "    \n",
    "    progress(1.0, desc=\"✅ Complete!\")\n",
    "    yield result, pipeline.get_summary()\n",
    "\n",
    "def generate_batch_synthetic(num_pairs: int, progress=gr.Progress()):\n",
    "    \"\"\"Generate batch with live updates after each entry\"\"\"\n",
    "    \n",
    "    results = []\n",
    "    count = int(num_pairs)\n",
    "    \n",
    "    for i in range(count):\n",
    "        # Update progress\n",
    "        progress_pct = (i + 1) / count\n",
    "        progress(progress_pct, desc=f\"🔄 Generating {i+1}/{count}...\")\n",
    "        \n",
    "        # Generate entry\n",
    "        entry = pipeline.generate_complete_entry()\n",
    "        pipeline.dataset.append(entry)\n",
    "        \n",
    "        # Format result\n",
    "        result = f\"### {i+1}. {entry['section_number']}: {entry['section_title']}\\n\"\n",
    "        result += f\"**Q:** {entry['question']}\\n\"\n",
    "        result += f\"**A:** {entry['answer']}\\n\\n\"\n",
    "        results.append(result)\n",
    "        \n",
    "        # Yield intermediate results to update UI in real-time\n",
    "        current_output = \"\".join(results)\n",
    "        current_output += f\"\\n---\\n⏳ **Progress: {i+1}/{count} completed**\"\n",
    "        \n",
    "        yield current_output, pipeline.get_summary()\n",
    "    \n",
    "    # Final output\n",
    "    final_output = \"\".join(results)\n",
    "    final_output += f\"\\n---\\n✅ **All {count} Q&A pairs generated successfully!**\"\n",
    "    \n",
    "    progress(1.0, desc=\"✅ Batch complete!\")\n",
    "    yield final_output, pipeline.get_summary()\n",
    "\n",
    "def save_synthetic_dataset():\n",
    "    \"\"\"Save the synthetic dataset\"\"\"\n",
    "    return pipeline.save_dataset()\n",
    "\n",
    "def clear_dataset():\n",
    "    \"\"\"Clear the current dataset\"\"\"\n",
    "    pipeline.dataset.clear()\n",
    "    return \"✅ Dataset cleared!\", pipeline.get_summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9d352fec",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cell 9: Enhanced UI with real-time updates\n",
    "with gr.Blocks(title=\"Synthetic Legal Q&A Generator\", theme=gr.themes.Soft()) as demo:\n",
    "    gr.Markdown(\"# 🤖 Synthetic Legal Q&A Data Generator\")\n",
    "    gr.Markdown(\"**Generates completely synthetic Indian legal sections AND Q&A pairs from scratch**\")\n",
    "    gr.Markdown(\"_Watch the magic happen in real-time! 🎬_\")\n",
    "    \n",
    "    with gr.Tab(\"🎯 Single Generation\"):\n",
    "        gr.Markdown(\"### Generate one synthetic legal section with Q&A\")\n",
    "        gr.Markdown(\"_See each step of generation as it happens_\")\n",
    "        \n",
    "        with gr.Row():\n",
    "            with gr.Column(scale=1):\n",
    "                topic_dropdown = gr.Dropdown(\n",
    "                    choices=LEGAL_TOPIC_SEEDS,\n",
    "                    label=\"🎯 Select Legal Topic\",\n",
    "                    value=LEGAL_TOPIC_SEEDS[0]\n",
    "                )\n",
    "                qtype_dropdown = gr.Dropdown(\n",
    "                    choices=QUESTION_TYPES,\n",
    "                    label=\"❓ Question Type\",\n",
    "                    value=QUESTION_TYPES[0]\n",
    "                )\n",
    "                gen_single_btn = gr.Button(\n",
    "                    \"🎲 Generate Synthetic Entry\", \n",
    "                    variant=\"primary\",\n",
    "                    size=\"lg\"\n",
    "                )\n",
    "            \n",
    "            with gr.Column(scale=2):\n",
    "                output_single = gr.Markdown(\n",
    "                    label=\"Generated Content\",\n",
    "                    value=\"Click **Generate** to create synthetic legal content...\"\n",
    "                )\n",
    "        \n",
    "        summary_single = gr.Textbox(\n",
    "            label=\"📊 Dataset Summary\", \n",
    "            lines=6,\n",
    "            interactive=False\n",
    "        )\n",
    "        \n",
    "        gen_single_btn.click(\n",
    "            fn=generate_single_synthetic,\n",
    "            inputs=[topic_dropdown, qtype_dropdown],\n",
    "            outputs=[output_single, summary_single]\n",
    "        )\n",
    "    \n",
    "    with gr.Tab(\"🚀 Batch Generation\"):\n",
    "        gr.Markdown(\"### Generate multiple synthetic legal Q&A pairs\")\n",
    "        gr.Markdown(\"_Live updates as each Q&A pair is created!_\")\n",
    "        \n",
    "        with gr.Row():\n",
    "            with gr.Column(scale=1):\n",
    "                num_slider = gr.Slider(\n",
    "                    minimum=5,\n",
    "                    maximum=1000,\n",
    "                    value=5,\n",
    "                    step=5,\n",
    "                    label=\"📦 Number of Synthetic Q&A Pairs\"\n",
    "                )\n",
    "                gr.Markdown(\"**Tip:** Start with 10-20 pairs to see live generation\")\n",
    "                gen_batch_btn = gr.Button(\n",
    "                    \"🔥 Generate Batch\", \n",
    "                    variant=\"primary\",\n",
    "                    size=\"lg\"\n",
    "                )\n",
    "            \n",
    "            with gr.Column(scale=2):\n",
    "                output_batch = gr.Markdown(\n",
    "                    label=\"Generated Synthetic Data\",\n",
    "                    value=\"Click **Generate Batch** to start creating multiple Q&A pairs...\"\n",
    "                )\n",
    "        \n",
    "        summary_batch = gr.Textbox(\n",
    "            label=\"📊 Dataset Summary\", \n",
    "            lines=6,\n",
    "            interactive=False\n",
    "        )\n",
    "        \n",
    "        gen_batch_btn.click(\n",
    "            fn=generate_batch_synthetic,\n",
    "            inputs=[num_slider],\n",
    "            outputs=[output_batch, summary_batch]\n",
    "        )\n",
    "    \n",
    "    with gr.Tab(\"💾 Manage Dataset\"):\n",
    "        gr.Markdown(\"### Save or Clear Your Synthetic Dataset\")\n",
    "        \n",
    "        with gr.Row():\n",
    "            with gr.Column():\n",
    "                gr.Markdown(\"**💾 Save your generated data**\")\n",
    "                gr.Markdown(\"Exports all Q&A pairs to `synthetic_legal_qa.json`\")\n",
    "                save_btn = gr.Button(\n",
    "                    \"💾 Save to JSON\", \n",
    "                    variant=\"primary\",\n",
    "                    size=\"lg\"\n",
    "                )\n",
    "            \n",
    "            with gr.Column():\n",
    "                gr.Markdown(\"**🗑️ Clear current dataset**\")\n",
    "                gr.Markdown(\"⚠️ This will remove all generated Q&A pairs\")\n",
    "                clear_btn = gr.Button(\n",
    "                    \"🗑️ Clear Dataset\", \n",
    "                    variant=\"stop\",\n",
    "                    size=\"lg\"\n",
    "                )\n",
    "        \n",
    "        manage_status = gr.Textbox(\n",
    "            label=\"Status\", \n",
    "            lines=2,\n",
    "            interactive=False\n",
    "        )\n",
    "        manage_summary = gr.Textbox(\n",
    "            label=\"Current Dataset Overview\", \n",
    "            lines=10,\n",
    "            interactive=False,\n",
    "            value=pipeline.get_summary()\n",
    "        )\n",
    "        \n",
    "        save_btn.click(\n",
    "            fn=save_synthetic_dataset,\n",
    "            inputs=[],\n",
    "            outputs=[manage_status]\n",
    "        )\n",
    "        \n",
    "        clear_btn.click(\n",
    "            fn=clear_dataset,\n",
    "            inputs=[],\n",
    "            outputs=[manage_status, manage_summary]\n",
    "        )\n",
    "    \n",
    "    # Footer\n",
    "    gr.Markdown(\"---\")\n",
    "    gr.Markdown(\"🎓 **LLM Engineering Week 3** | Synthetic Data Generation Challenge\")\n",
    "\n",
    "demo.launch(share=False, inbrowser=True)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "llm-engineering",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
